/************************************************************/
// Class StickyScroller
// Purpose: Create a fixed scroller
// Parameters:
//  obj: The object that will be scrolling
//  start: What distance from the top (in px) the effect starts
//  end: What distance from the top (in px) the effect ends
//  interval: What scroll distance triggers the callback
//  range: How many pixels after the
//  margin: Margin from the top of the browser
//  distance: How far the obj will move
/************************************************************/

function StickyScroller(c,d){var e=this;var top=$(c).css('top');var f=$(c).css('left');var g=$(c).css('right');var scroll=0;var h=0;var i={start:0,end:1000,interval:400,margin:parseInt(top,10),range:400},j=jQuery.extend(i,d);c=$(c);j.index=0;j.oldIndex=0;GetSet.getters({scope:e,obj:j});var k={};k.newIndex=function(){};k.limbo=function(){};k.scroll=function(){};GetSet.setters({scope:this,prefix:"on",obj:k});this.distanceFrom=function(n){h=$(window).scrollTop();var top=n*j.interval;var o=n*j.interval+j.range;var p=Math.abs(h-top);var q=Math.abs(h-o);if(p<q){return p;}else{return q;}};this.closestIndex=function(){if(j.index===0){return 1;}var n=this.distanceFrom(j.index-1);var o=this.distanceFrom(j.index+1);if(n<=o){return j.index-1;}else{return j.index+1;}};var l=function(){h=$(window).scrollTop()+j.margin;if(h>j.start&&h<j.end){tempIndex=Math.floor((h-j.start)/j.interval);if(tempIndex!==j.index){j.oldIndex=j.index;j.index=tempIndex;}}else if(h>=j.end){j.oldIndex=j.index;j.index=Math.floor((j.end-j.start)/j.interval);}else{j.oldIndex=j.index;j.index=0;}};this.firstIndex=function(){return 0;};this.lastIndex=function(){return Math.floor((j.end-j.start)/j.interval);};this.inRange=function(){var scroll=$(window).scrollTop()-j.start+j.margin;var n=(scroll>=j.index*j.interval)&&(scroll<=(j.index*j.interval+j.range));return n;};var m=$('<div id="scrollcontainer">').css({width:c.width(),height:c.height(),position:"absolute"});c.wrap(m);$(window).scroll(function(){scroll=$(window).scrollTop()+j.margin;l();if(scroll<j.start){$(c).css({position:'absolute',top:0,left:0,right:0});$("#scrollcontainer").css({position:'absolute',top:j.start,left:f,right:g});}else if(scroll>j.end){$(c).css({position:'absolute',top:0,left:0,right:0});$("#scrollcontainer").css({position:'absolute',top:j.end,left:f,right:g});}else{$(c).css({position:'fixed',top:j.margin,left:f,right:g});}if(j.oldIndex!==j.index){k.newIndex(j.index);}if(!e.inRange()&&scroll>j.start&&scroll<j.end){k.limbo(j.index);}k.scroll(j.index);});}


/************************************************************/
//Class GetSet
//Purpose: Creates dynamic getters and setters
/************************************************************/

var GetSet = {};

//=========================================================//
//Public Method override
//Purpose: Override default values through iteration
//Parameters:
//  obj: The object whose default values will be overridden
//Postcondition: options Object is altered
//=========================================================//
GetSet.override = function(options, defaults)
{
    //Store this scope
    var $this = options;
    
    
    for (var i in defaults)
    {
        if(!($this[i]))
        {
            $this[i] = defaults[i];
        }        
    }
};

//=========================================================//
//Public getFunctions
//Purpose: Copies one objects functions to another
//Parameters:
//  template: The object whose method will be copied
//  recepient: The object receiving the template methods
//Postcondition: recepient object is altered
//=========================================================//
GetSet.getFunctions = function(template, recepient)
{    
    for (var i in template)
    {
        if(template[i].constructor == Function)
        {
            recepient[i] = template[i];
        }
    }
};


//=========================================================//
//Public Method gettters
//Purpose: Dynamically creates accessor methods(getters)
//Parameters: 
//  scope: The scope in which the accessor methods will be
//         applied
//  prefix: Goes before the property. i.e. (get)Name
//  camel: whether to induce camel case
//  obj: Accessors
//Postcondition: scope has been altered to include
//accessor methods
//=========================================================//
GetSet.getters = function(options)
{   
    //Over-ride default values
    var defaults =
    {
        prefix: "get",
        camel: true
    };
    
    //Override defaults values
    GetSet.override(options, defaults);
    
    //If prefix is set to 'none', force blank. A blank string as a parameter
    //evaluates to null for some reason.
    options.prefix = (options.prefix === "none") ? "" : options.prefix;
    
    //Iterate through the properties of the object
    var str;
    for ( var i in options.obj )
    {
        //If camel case is enabled and no blank prefix
        if(options.camel && options.prefix != "")
        {
            str = i.charAt(0).toUpperCase() + i.substr(1);
        }
        else
        {
            str = i;
        }
        (function(i)
        {
                // Dynamically create an accessor method
                options.scope[ options.prefix + str ] = function()
                {
                        return options.obj[i];
                };  
            })(i);
    }
};

//=========================================================//
//Public Method setters
//Purpose: Dynamically creates muator methods(setters)
//Parameters: 
//  scope: The scope in which the mutator methods will be
//         applied
//  prefix: Goes before the property. i.e. (set)Name
//  camel: whether to induce camel case
//  obj: The object that will have mutators
//Postcondition: scope has been altered to include mutator
//methods
//=========================================================//
GetSet.setters = function(options)
{
    //Over-ride default values
    var defaults =
    {
        prefix: "set",
        camel: true
    };
    
    //Override defaults values
    GetSet.override(options, defaults);
    
    //If prefix is set to 'none', force blank. A blank string as a parameter
    //evaluates to null for some reason.
    options.prefix = (options.prefix === "none") ? "" : options.prefix;    
    
    //Iterate through the properties of the object
    var str;
    for ( var i in options.obj )
    {
        //If camel case is enabled and no blank prefix
        if(options.camel && options.prefix != "")
        {
            str = i.charAt(0).toUpperCase() + i.substr(1);
        }
        else
        {
            str = i;
        }
        (function(i)
        {
                // Dynamically create an accessor method
                options.scope[ options.prefix + str ] = function(val)
                {
                       options.obj[i] = val;
                };  
            })(i);
    }
};